package com.gitee.wsl.base

import com.gitee.wsl.func.consumer.BiConsumer
import com.gitee.wsl.func.BiFunction
import com.gitee.wsl.func.predicate.BiPredicate
import com.gitee.wsl.func.Function
import com.gitee.wsl.func.Supplier

/**
 * An Option pair of values; either the pair is present with both values, or is absent with no
 * value.
 *
 *
 * This is essentially an `Option<Pair>` that users can access through fluent methods
 * like [.map], [.filter], [.or] instead of opaque names like
 * `first`, `second`.
 *
 * @since 5.0
 */
abstract class BiOptional<A, B> {
    /**
     * If a pair of values is present, apply `mapper` to them, and if the result is non-null,
     * return an `Option` containing it. Otherwise return an empty `Option`.
     *
     * @throws NullPointerException if `mapper` is null
     */
    abstract fun <T > map(mapper: BiFunction<in A, in B, out T>): Option<T>

    /**
     * If a pair of values is present, apply `aMapper` and `bMapper` to them, and if both
     * results are non-null, return a new `BiOptional` instance containing the results.
     * Otherwise return an empty `BiOptional`.
     *
     * @throws NullPointerException if `aMapper` or `bMapper` is null
     */
    abstract fun <A2, B2> map(
        aMapper: BiFunction<in A, in B, out A2>,
        bMapper: BiFunction<in A, in B, out B2>
    ): BiOptional<A2, B2>

    /**
     * If a pair of values is present, apply `aMapper` and `bMapper` to each respectively,
     * and if both results are non-null, return a new `BiOptional` instance containing the
     * results. Otherwise return an empty `BiOptional`.
     *
     *
     * The following example parses and evaluates a numeric comparison:
     *
     * <pre>`if (Substring.first('=')
     * .splitThenTrim("10 = 20")
     * .map(Ints::tryParse, Ints::tryParse)
     * .filter((a, b) -> a.equals(b))
     * .isPresent()) {
     * // The two numbers are equal.
     * }
    `</pre> *
     *
     * @throws NullPointerException if `aMapper` or `bMapper` is null
     */
    abstract fun <A2, B2> map(
        aMapper: Function<in A, out A2>, bMapper: Function<in B, out B2>
    ): BiOptional<A2, B2>

    /**
     * If a pair of values is present, apply the `BiOptional`-bearing `mapper` to them,
     * and return that result. Otherwise, return an empty `BiOptional`.
     *
     * @throws NullPointerException if `mapper` is null or returns a null result
     */
    abstract fun <T> flatMap(
        mapper: BiFunction<in A, in B, out Option<T>>
    ): Option<T>

    /**
     * If a pair of values is present and matches `predicate`, return this `BiOptional
     * instance`. Otherwise, return an empty `BiOptional`.
     *
     * @throws NullPointerException if `predicate` is null
     */
    abstract fun filter(predicate: BiPredicate<in A, in B>): BiOptional<A, B>

    /**
     * If a pair of values is present and matches `predicate`, the pair is skipped (returns empty).
     *
     * @throws NullPointerException if `predicate` is null
     * @since 6.6
     */
    fun skipIf(predicate: BiPredicate<in A, in B>): BiOptional<A, B> {
        return filter(predicate.negate())
    }

    /**
     * Invokes `consumer` with the pair if present and returns this object as is.
     *
     * @throws NullPointerException if consumer is null
     * @since 5.1
     */
    abstract fun peek(consumer: BiConsumer<in A, in B>): BiOptional<A, B>

    /**
     * Returns true if the pair exists and satisfies the `condition` predicate.
     *
     * @since 5.7
     */
    abstract fun matches(condition: BiPredicate<in A, in B>): Boolean

    /**
     * If a pair of values is present, invoke `consumer` with them. Otherwise, do nothing.
     *
     * @throws NullPointerException if consumer is null
     */
    abstract fun ifPresent(consumer: BiConsumer<in A, in B>)

    /** Returns true if the pair of values is present.  */
    abstract val isPresent: Boolean

    /**
     * If a pair of values are present, return this `BiOptional` instance. Otherwise, returns a
     * `BiOptional` produced by `alternative`.
     */
    abstract fun or(
        alternative: Supplier<out BiOptional<out A, out B>>
    ): BiOptional<A, B>

    /**
     * Returns the pair if present, or else returns `(a, b)`. `a` and `b`
     * are allowed to be null.
     *
     * @since 5.1
     */
    abstract fun orElse(a: A, b: B): Both<A, B>

    /**
     * Ensures that the pair must be present or else throws [NoSuchElementException].
     *
     * @since 5.1
     */
    abstract fun orElseThrow(): Both<A, B>

    /**
     * Ensures that the pair must be present or else throws the exception returned by
     * `exceptionSupplier`.
     *
     * @throws NullPointerException if `exceptionSupplier` is null, or returns null.
     * @throws E if the pair is absent.
     * @since 5.1
     */

    abstract fun <E : Throwable> orElseThrow(exceptionSupplier: Supplier<E>): Both<A, B>

    /**
     * Ensures that the pair must be present or else throws the exception returned by `exceptionFactory` with `message` formatted with `args` using [ ][Strings.lenientFormat].
     *
     * @throws NullPointerException if `exceptionFactory` or `message` is null, or if
     * `exceptionFactory` returns null.
     * @throws E if the pair is absent.
     * @since 6.6
     */

    fun <E : Throwable> orElseThrow(
        exceptionFactory: Function<String, E>, message: String
    ): Both<A, B> {

        if (isPresent) {
            return orElseThrow()
        }
        throw exceptionFactory.apply(message)
    }

    /** Returns a `BiStream` view of this BiOptional.  */
    //abstract fun stream(): BiStream<A, B>?
     class Present<A, B>(private val a: A, private val b: B, override val isPresent: Boolean=true) : BiOptional<A, B>(), Both<A, B> {
        override fun <T > map(mapper: BiFunction<in A, in B, out T>): Option<T> {
            return Option.ofNullable(mapper.apply(a, b))
        }

        override fun <A2, B2> map(
            aMapper: BiFunction<in A, in B, out A2>,
            bMapper: BiFunction<in A, in B, out B2>
        ): BiOptional<A2, B2> {
            val a2: A2 = aMapper.apply(a, b) ?: return empty()
            val b2: B2 = bMapper.apply(a, b) ?: return empty()
            return of(a2, b2)
        }

        override fun <A2, B2> map(
            aMapper: Function<in A, out A2>, bMapper: Function<in B, out B2>
        ): BiOptional<A2, B2> {
            val a2: A2 = aMapper.apply(a) ?: return empty()
            val b2: B2 = bMapper.apply(b) ?: return empty()
            return of(a2, b2)
        }

        override fun <T> flatMap(
            mapper: BiFunction<in A, in B, out Option<T>>
        ): Option<T> {
            return covariant(mapper.apply(a, b))
        }

        override fun filter(predicate: BiPredicate<in A, in B>): BiOptional<A, B> {
            return if (predicate.test(a, b)) this else empty()
        }

        override fun matches(predicate: BiPredicate<in A, in B>): Boolean {
            return predicate.test(a, b)
        }

        override fun or(
            alternative: Supplier<out BiOptional<out A, out B>>
        ): BiOptional<A, B> {
            return this
        }

        override fun orElse(a: A, b: B): Both<A, B> {
            return this
        }



        override fun orElseThrow(): Both<A, B> {
            return this
        }

        override fun <E : Throwable> orElseThrow(exceptionSupplier: Supplier<E>): Both<A, B> {
            return this
        }

        override fun peek(consumer: BiConsumer<in A, in B>): Present<A, B> {
            consumer.accept(a, b)
            return this
        }

        override fun ifPresent(consumer: BiConsumer<in A, in B>) {
            consumer.accept(a, b)
        }


        /*fun isPresent(): Boolean {
           return true
       }*/

        /*override fun stream(): BiStream<A, B> {
            return BiStream.of(a, b)
        }*/

        /*override fun equals(obj: Any?): Boolean {
            if (obj is Present<*, *>) {
                val that = obj
                return a == that.a && b == that.b
            }
            return false
        }

        override fun hashCode(): Int {
            return Objects.hash(a, b)
        }*/

        override fun toString(): String {
            return "of($a, $b)"
        }

        override fun <T> andThen(mapper: BiFunction<in A, in B, T>): T {
            return mapper.apply(a, b)
        }
    }

    companion object {
        /** Returns an empty `BiOptional` instance.  */
        // EMPTY contains no A or B.
        fun <A, B> empty(): BiOptional<A, B> {
            return EMPTY as BiOptional<A, B>
        }

        /**
         * Returns a `BiOptional` containing the pair `(a, b)`.
         *
         * @throws NullPointerException if either `a` or `b` is null
         */
        fun <A, B> of(a: A, b: B): BiOptional<A, B> {
            return Present(a, b)
        }

        /*@Deprecated("Use {@link Optionals#both} instead. ")
        fun <A, B> both(a: Option<A>?, b: Option<B>?): BiOptional<A, B> {
            return Optionals.both(a, b)
        }*/

        /**
         * If `Option` is present with value `v`, adapts it to `BiOptional` containing
         * values `(v, v)`.
         *
         * @throws NullPointerException if `Option` is null.
         */
        fun <T> from(option: Option<T>): BiOptional<T, T> {
            return if (option.isSome()) of(option.get(), option.get()) else empty()
        }

        /**
         * Maps the value contained in `Option` to a `BiOptional` using `mapper`, or
         * else returns empty. For example, the following code uses [Substring.Pattern.split] to
         * split an Option string:
         *
         * <pre>`Option<KeyValue> keyValue =
         * BiOptional.flatMap(getOptionalInput(), Substring.first(':')::split)
         * .map(KeyValue::new);
        `</pre> *
         *
         *
         * Use this method to bridge from an `Option` to `BiOptional` chain.
         *
         * @throws NullPointerException if `Option` or `mapper` is null, or if `mapper` returns null.
         */
        /*fun <T, A, B> flatMap(
            option: Option<T>,
            mapper: Func<in T, out BiOptional<out A, out B>>
        ): BiOptional<A, B> {
            return option.map(mapper).map { Option: BiOptional<out A, out B> ->
                covariant(
                    Option
                )
            }.orElse(empty())
        }


        */

        fun <T> covariant(option: Option< T>): Option<T> {
            return option
        }

        fun <A, B> covariant(option: BiOptional<out A, out B>): BiOptional<A, B> {
            return option as BiOptional<A, B>
        }

        private val EMPTY: BiOptional<Any, Any> = object : BiOptional<Any, Any>() {
            override fun <T > map(mapper: BiFunction<in Any, in Any, out T>): Option<T> {
                return Option.empty()
            }

            override fun <A2, B2> map(
                aMapper: BiFunction<in Any, in Any, out A2>,
                bMapper: BiFunction<in Any, in Any, out B2>
            ): BiOptional<A2, B2> {
                return empty()
            }

            override fun <A2, B2> map(
                aMapper: Function<in Any, out A2>, bMapper: Function<in Any, out B2>
            ): BiOptional<A2, B2> {
                return empty()
            }


            override fun filter(predicate: BiPredicate<in Any, in Any>): BiOptional<Any, Any> {
                return this
            }

            override fun matches(condition: BiPredicate<in Any, in Any>): Boolean {
                return false
            }

            override fun or(alternative: Supplier<out BiOptional<out Any, out Any>>): BiOptional<Any, Any> {
                return covariant(alternative.get())
            }

            override fun orElse(a: Any, b: Any): Both<Any, Any> {
                return Present(a, b)
            }

            override fun orElseThrow(): Both<Any, Any> {
                throw NoSuchElementException()
            }


            override fun <E : Throwable> orElseThrow(exceptionSupplier: Supplier<E>): Both<Any, Any> {
                throw exceptionSupplier.get()
            }

            override fun <T> flatMap(mapper: BiFunction<in Any, in Any, out Option<T>>): Option<T> {
                return Option.empty()
            }

            override fun peek(consumer: BiConsumer<in Any, in Any>): BiOptional<Any, Any> {
                return this
            }

            override fun ifPresent(consumer: BiConsumer<in Any, in Any>) {
            }



            /*fun peek(consumer: BiConsumer<Any, Any>): BiOptional<Any, Any> {
                Objects.requireNonNull(consumer)
                return this
            }

            fun ifPresent(consumer: BiConsumer<Any, Any>) {
                Objects.requireNonNull(consumer)
            }*/

            override val isPresent: Boolean
                get() = false



            /*override fun stream(): BiStream<Any, Any> {
                return BiStream.empty()
            }*/

            override fun toString(): String {
                return "empty()"
            }
        }
    }
}


